package com.gobang.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis通用工具类
 * 基于StringRedisTemplate实现，所有操作的键值均为字符串类型
 */
@Component
public class RedisUtil {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // ========== 通用操作 ==========

    /**
     * 设置缓存过期时间
     *
     * @param key     键
     * @param timeout 过期时间（秒）
     * @return true=成功, false=失败
     */
    public boolean expire(String key, long timeout) {
        try {
            if (timeout > 0) {
                stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 设置缓存过期时间（自定义时间单位）
     *
     * @param key      键
     * @param timeout  过期时间
     * @param timeUnit 时间单位
     * @return true=成功, false=失败
     */
    public boolean expire(String key, long timeout, TimeUnit timeUnit) {
        try {
            if (timeout > 0) {
                stringRedisTemplate.expire(key, timeout, timeUnit);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取过期时间（秒）
     *
     * @param key 键
     * @return 过期时间（秒），-1=永久有效，-2=键不存在
     */
    public long getExpire(String key) {
        Long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
        return expire != null ? expire : -2;
    }

    /**
     * 判断键是否存在
     *
     * @param key 键
     * @return true=存在, false=不存在
     */
    public boolean hasKey(String key) {
        try {
            Boolean hasKey = stringRedisTemplate.hasKey(key);
            return hasKey != null && hasKey;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param keys 可以传一个值或多个
     */
    public void delete(String... keys) {
        if (keys != null && keys.length > 0) {
            if (keys.length == 1) {
                stringRedisTemplate.delete(keys[0]);
            } else {
                stringRedisTemplate.delete(Arrays.asList(keys));
            }
        }
    }

    // ========== String操作 ==========

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public String get(String key) {
        return key == null ? null : stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 设置缓存
     *
     * @param key   键
     * @param value 值
     * @return true=成功, false=失败
     */
    public boolean set(String key, String value) {
        try {
            stringRedisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 设置缓存并设置过期时间
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间（秒）
     * @return true=成功, false=失败
     */
    public boolean set(String key, String value, long timeout) {
        try {
            if (timeout > 0) {
                stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 设置缓存并设置过期时间（自定义时间单位）
     *
     * @param key      键
     * @param value    值
     * @param timeout  过期时间
     * @param timeUnit 时间单位
     * @return true=成功, false=失败
     */
    public boolean set(String key, String value, long timeout, TimeUnit timeUnit) {
        try {
            if (timeout > 0) {
                stringRedisTemplate.opsForValue().set(key, value, timeout, timeUnit);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 递增因子（大于0）
     * @return 递增后的值
     */
    public long increment(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        Long result = stringRedisTemplate.opsForValue().increment(key, delta);
        return result != null ? result : 0;
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 递减因子（大于0）
     * @return 递减后的值
     */
    public long decrement(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        Long result = stringRedisTemplate.opsForValue().increment(key, -delta);
        return result != null ? result : 0;
    }

    // ========== Hash操作 ==========

    /**
     * 获取Hash中的值
     *
     * @param key  键（不能为null）
     * @param item 项（不能为null）
     * @return 值
     */
    public String hGet(String key, String item) {
        Object value = stringRedisTemplate.opsForHash().get(key, item);
        return value != null ? value.toString() : null;
    }

    /**
     * 获取Hash中的所有键值对
     *
     * @param key 键
     * @return 键值对Map
     */
    public Map<Object, Object> hGetAll(String key) {
        return stringRedisTemplate.opsForHash().entries(key);
    }

    /**
     * 设置Hash
     *
     * @param key 键
     * @param map 键值对Map
     * @return true=成功, false=失败
     */
    public boolean hSet(String key, Map<String, String> map) {
        try {
            stringRedisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 设置Hash并设置过期时间
     *
     * @param key     键
     * @param map     键值对Map
     * @param timeout 过期时间（秒）
     * @return true=成功, false=失败
     */
    public boolean hSet(String key, Map<String, String> map, long timeout) {
        try {
            stringRedisTemplate.opsForHash().putAll(key, map);
            if (timeout > 0) {
                expire(key, timeout);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向Hash表中放入数据，如果不存在则创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true=成功, false=失败
     */
    public boolean hSet(String key, String item, String value) {
        try {
            stringRedisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向Hash表中放入数据并设置过期时间
     *
     * @param key     键
     * @param item    项
     * @param value   值
     * @param timeout 过期时间（秒）
     * @return true=成功, false=失败
     */
    public boolean hSet(String key, String item, String value, long timeout) {
        try {
            stringRedisTemplate.opsForHash().put(key, item, value);
            if (timeout > 0) {
                expire(key, timeout);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除Hash表中的值
     *
     * @param key   键
     * @param items 项（可以多个，不能为null）
     */
    public void hDelete(String key, Object... items) {
        stringRedisTemplate.opsForHash().delete(key, items);
    }

    /**
     * 判断Hash表中是否有该项的值
     *
     * @param key  键
     * @param item 项
     * @return true=存在, false=不存在
     */
    public boolean hHasKey(String key, String item) {
        return stringRedisTemplate.opsForHash().hasKey(key, item);
    }

    // ========== Set操作 ==========

    /**
     * 获取Set中的所有值
     *
     * @param key 键
     * @return Set集合
     */
    public Set<String> sGet(String key) {
        try {
            return stringRedisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return new HashSet<>();
        }
    }

    /**
     * 根据value查询Set中是否存在
     *
     * @param key   键
     * @param value 值
     * @return true=存在, false=不存在
     */
    public boolean sHasKey(String key, String value) {
        try {
            Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, value);
            return isMember != null && isMember;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入Set缓存
     *
     * @param key    键
     * @param values 值（可以多个）
     * @return 成功个数
     */
    public long sSet(String key, String... values) {
        try {
            Long count = stringRedisTemplate.opsForSet().add(key, values);
            return count != null ? count : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将数据放入Set缓存并设置过期时间
     *
     * @param key     键
     * @param timeout 过期时间（秒）
     * @param values  值（可以多个）
     * @return 成功个数
     */
    public long sSet(String key, long timeout, String... values) {
        try {
            Long count = stringRedisTemplate.opsForSet().add(key, values);
            if (timeout > 0) {
                expire(key, timeout);
            }
            return count != null ? count : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取Set缓存的长度
     *
     * @param key 键
     * @return 长度
     */
    public long sGetSize(String key) {
        try {
            Long size = stringRedisTemplate.opsForSet().size(key);
            return size != null ? size : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除Set中的值
     *
     * @param key    键
     * @param values 值（可以多个）
     * @return 移除的个数
     */
    public long sRemove(String key, Object... values) {
        try {
            Long count = stringRedisTemplate.opsForSet().remove(key, values);
            return count != null ? count : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ========== ZSet（有序集合）操作 ==========

    /**
     * 添加到有序集合
     *
     * @param key   键
     * @param value 值
     * @param score 分数
     * @return true=成功, false=失败
     */
    public boolean zAdd(String key, String value, double score) {
        try {
            Boolean result = stringRedisTemplate.opsForZSet().add(key, value, score);
            return result != null && result;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取有序集合的大小
     *
     * @param key 键
     * @return 大小
     */
    public long zSize(String key) {
        try {
            Long size = stringRedisTemplate.opsForZSet().size(key);
            return size != null ? size : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取有序集合中指定范围的成员（按分数从小到大）
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置
     * @return 成员集合
     */
    public Set<String> zRange(String key, long start, long end) {
        try {
            return stringRedisTemplate.opsForZSet().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return new HashSet<>();
        }
    }

    /**
     * 根据分数范围获取有序集合成员
     *
     * @param key      键
     * @param minScore 最小分数
     * @param maxScore 最大分数
     * @return 成员集合
     */
    public Set<String> zRangeByScore(String key, double minScore, double maxScore) {
        try {
            return stringRedisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore);
        } catch (Exception e) {
            e.printStackTrace();
            return new HashSet<>();
        }
    }

    /**
     * 根据分数范围获取有序集合成员（带分页）
     *
     * @param key      键
     * @param minScore 最小分数
     * @param maxScore 最大分数
     * @param offset   偏移量
     * @param count    数量
     * @return 成员集合
     */
    public Set<String> zRangeByScore(String key, double minScore, double maxScore, long offset, long count) {
        try {
            return stringRedisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore, offset, count);
        } catch (Exception e) {
            e.printStackTrace();
            return new HashSet<>();
        }
    }

    /**
     * 获取成员的分数
     *
     * @param key   键
     * @param value 值
     * @return 分数
     */
    public Double zScore(String key, String value) {
        try {
            return stringRedisTemplate.opsForZSet().score(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 移除有序集合中的成员
     *
     * @param key    键
     * @param values 值（可以多个）
     * @return 移除的个数
     */
    public long zRemove(String key, Object... values) {
        try {
            Long count = stringRedisTemplate.opsForZSet().remove(key, values);
            return count != null ? count : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ========== List操作 ==========

    /**
     * 获取List缓存的内容
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置（0到-1代表所有值）
     * @return List集合
     */
    public List<String> lGet(String key, long start, long end) {
        try {
            return stringRedisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 获取List缓存的长度
     *
     * @param key 键
     * @return 长度
     */
    public long lGetSize(String key) {
        try {
            Long size = stringRedisTemplate.opsForList().size(key);
            return size != null ? size : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引获取List中的值
     *
     * @param key   键
     * @param index 索引（0开始，-1表示倒数第一个，-2表示倒数第二个...）
     * @return 值
     */
    public String lGetIndex(String key, long index) {
        try {
            return stringRedisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将值放入List缓存（右侧）
     *
     * @param key   键
     * @param value 值
     * @return true=成功, false=失败
     */
    public boolean lSet(String key, String value) {
        try {
            stringRedisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将值放入List缓存并设置过期时间
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间（秒）
     * @return true=成功, false=失败
     */
    public boolean lSet(String key, String value, long timeout) {
        try {
            stringRedisTemplate.opsForList().rightPush(key, value);
            if (timeout > 0) {
                expire(key, timeout);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将List放入缓存
     *
     * @param key   键
     * @param value List值
     * @return true=成功, false=失败
     */
    public boolean lSet(String key, List<String> value) {
        try {
            stringRedisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将List放入缓存并设置过期时间
     *
     * @param key     键
     * @param value   List值
     * @param timeout 过期时间（秒）
     * @return true=成功, false=失败
     */
    public boolean lSet(String key, List<String> value, long timeout) {
        try {
            stringRedisTemplate.opsForList().rightPushAll(key, value);
            if (timeout > 0) {
                expire(key, timeout);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改List中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return true=成功, false=失败
     */
    public boolean lUpdateIndex(String key, long index, String value) {
        try {
            stringRedisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除List中N个值为value的元素
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, String value) {
        try {
            Long remove = stringRedisTemplate.opsForList().remove(key, count, value);
            return remove != null ? remove : 0;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}

